package com.digital.welcome.aspect;

import com.digital.welcome.entity.OperateLog;
import com.digital.welcome.service.OperateLogService;
import com.digital.welcome.util.IpUtil;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

@Aspect
@Component("operationLogAspect")
@Slf4j
public class OperationLogAspect
{
    @Autowired
    private OperateLogService operateLogService;

    // 配置织入点(以OperationLog注解为标志),捕获 controller下面的类的方法
    @Pointcut("@annotation(com.digital.welcome.annotation.OperationLog)")
    public void logPointCut() {
    }
    /*
    后置通知 用于拦截操作，在方法返回后执行
    @param joinPoint 切点
    */
    @AfterReturning(pointcut = "logPointCut()")
    public void doAfterReturn(JoinPoint joinPoint)
    {
        handleLog(joinPoint, null);
    }
    /*
    拦截异常操作，有异常时执行
    @param joinPoint
    @param e
    */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrow(JoinPoint joinPoint, Exception e)
    {
        handleLog(joinPoint, e);
    }

    private void handleLog(JoinPoint joinPoint, Exception e) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取 HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        // 获得请求的Url
        StringBuffer requestURL = request.getRequestURL();
        String requestMethod = request.getMethod();

        // 获得Ip地址
        String ipAddress = IpUtil.getIPAddress(request);
        // 访问类名
        String className = joinPoint.getTarget().getClass().getSimpleName();
        // 从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取切入点所在的方法
        Method method = signature.getMethod();
        // 获取方法名称
        String methodName = method.getName();
        // 获得方法的参数类型
        Class<?>[] methodParameterTypes = method.getParameterTypes();
        StringBuffer parameters = new StringBuffer();
        for (Class<?> parameterType : methodParameterTypes)
        {
            if (parameters.length()==0)
            {
                parameters.append(parameterType.getSimpleName() + ".class");//得到类的简写名称
            }
            else
            {
                parameters.append("," + parameterType.getSimpleName() + ".class");
            }
        }
        String parameterTypes = parameters.toString();
        // 获得注解中的参数的内容
        ApiOperation annotation = method.getAnnotation(ApiOperation.class);
        String describe = null;

        if (annotation==null)
        {
            describe = "无信息";
        }
        else
        {
            describe = annotation.value();
        }
        // 向数据库中写入数据
        insertOperationLog(className,methodName,parameterTypes,requestURL.toString(),ipAddress,describe);
        log.info("【"+describe+"】"+" 类名【"+className+"】 方法名【"+methodName+"】 参数名【"+parameterTypes+"】 ip【"+ipAddress+"】 请求url【"+requestURL.toString()+"】");
    }
    @Async
    public void insertOperationLog(String className, String methodName, String parameterTypes, String requestUrl, String ip, String describe) {

        LocalDateTime localDateTime = LocalDateTime.now();
        // 生成一个操作信息类
        OperateLog operateLog = new OperateLog();
        // 将操作信息插入数据库
        operateLog.setClassName(className);
        operateLog.setFunctionName(methodName);
        operateLog.setParameter(parameterTypes);
        operateLog.setUrl(requestUrl);
        operateLog.setIp(ip);
        operateLog.setRemark(describe);
        operateLog.setTime(localDateTime);
        operateLogService.insertOperateLog(operateLog);
    }
}
